from Commons import *
import libtcodpy as L
appended = 0
removed = 0



class Attack:#an attack
	def __init__(self,damage):
		self.damage = damage
#factions
FAC_INDEPENDENT = -1#attacks anyone
FAC_PEACEFUL = -2#does not attack unless provoked
class Creature(ChronosObject):#a creature
	dead = False
	def __init__(self):
		self.x = -1
		self.y = -1
		self.ascii = '?'
		self.color = 0
		self.hp = 200#hit points
		self.damage = 1
		#XXX: Need to design a combat system
		self.healrate = 0#rate at which the monster heals lost hp
		self.bleedrate = 0#rate at which the monster loses hp -- caused by attacks
		self.healbleedingrate = 0#rate at which the monster
		self.adreneline = 0#slowly goes up when a creature is fighting, allows it to do special attacks
		self.ai = CreatureAI(self)
	def initCreature(self,x,y,ascii,color):#automatically adds the creature
		self.x = x
		self.y = y
		self.ascii = ascii
		self.color = color
		self.faction = Game()[A_RNG].nextInt(0,1)
		Game()[A_MAP][L_CREATURES].addtoqueuewithdelay(self,100)
		Game()[A_MAP][L_CREATURES].addCreature(self)#XXX: keep automatic adding
	def loadCreature(self,data):#each data element is split, adds creature
		self.x = int(data[0])
		self.y = int(data[1])
		self.ascii = data[2]
		self.color = L.red
		Game()[A_MAP][L_CREATURES].addtoqueuewithdelay(self,int(data[3]))
		Game()[A_MAP][L_CREATURES].addCreature(self)
		self.faction = int(data[4])
		self.hp = int(data[5])
		self.damage = int(data[6])
	def saveCreature(self):#saves the creature data elements
		return str(self.x) + "," + str(self.y) + "," + str(self.ascii) + "," + str(self.assignedtime - Game()[A_MAP][L_CREATURES].time)+","+str(self.faction)+","+str(self.hp)+","+str(self.damage)+"\n"
	def move(self, xdirect, ydirect):#moves the creature -- returns True if the move successful
		if (self.dead):#the creature can't move if dead
			return False
		tmp_x = self.x+xdirect
		tmp_y = self.y+ydirect
		if (tmp_x < 0 or tmp_x >= Game()[A_MAP].xlength or tmp_y < 0 or tmp_y >= Game()[A_MAP].ylength):#the creature can't move outside the edge of the map
			return False
		if (not Game()[A_MAP][L_TILES][tmp_x][tmp_y].isWalkable()):#the tile must be walkable for the creature to move to it
			return False
		if (Game()[A_MAP][L_CREATURES][Point(tmp_x,tmp_y)] != None):#if there is a creature there
			c = Game()[A_MAP][L_CREATURES][Point(tmp_x,tmp_y)]
			if (self.canAttack(c)):#choose if attacking
				c.hp -= Game()[A_RNG].nextInt(0,self.damage)#between 0 and self's damage
				if (c.hp <= 0):
					c.die()#attacks do 1 point of damage
				return True#creature took their turn
			return False#no attack no move, creature is a friendly
		
		self.x = tmp_x#move the creature
		self.y = tmp_y
		return True#success

	#decide if one creature can attack another,  FAC_PEACEFUL can never do so, FAC_INDEPENDENT always does it, and otherwise two of the same faction never does it
	def canAttack(self,c):
		return (self.faction != FAC_PEACEFUL and (self.faction == FAC_INDEPENDENT or self.faction != c.faction))

	def die(self):#kill the creature
		self.dead = True#is dead
		self.receivenotifications = False
		self.ai.handleDeath()#let the ai decide what to do with the creature's death
	def notify(self):#the ai chooses the turn when the creature is ready to act
		return self.ai.takeTurn()

	def AIOverride(self,overridingai):#change the ai and override it
		self.latentai = self.ai
		self.ai = overridingai
		#notify the ai of any death that may have occured over the ai switch
		if (self.dead):
			self.ai.handleDeath()

	def UndoAIOverride(self):#undo the ai override
		self.ai = self.latentai
		#notify the ai of any death that may have occured
		if (self.dead):
			self.ai.handleDeath()
			
		
			
class GroupAI:#forces all creatures to go along a line to a point, attacking creatures on their way if able
	def __init__(self,xpoint,ypoint):
		self.xpoint = xpoint
		self.ypoint = ypoint
	def attackNearby(self,creature):
		for direction in DIRECTIONS:#scan adjacent creatures
			c = Game()[A_MAP][L_CREATURES][Point(creature.x+direction[0],creature.y+direction[1])]
			if (c != None and creature.canAttack(c) and creature.move(direction[0],direction[1])):#if it can attack and move there
				return True
		return False
	def handleCreature(self,creature):
		if (self.attackNearby(creature)):
			return False#keep going
		if (creature.x == self.xpoint and creature.y == self.ypoint):
			return True#relinquish control once the point is reached
		#find the first point of the bresham's line algorithm, using libtcod
		L.line_init(creature.x,creature.y,self.xpoint,self.ypoint)
		(x,y) = L.line_step()
		creature.move(x-creature.x,y-creature.y)
		return False#keep going
	def saveGroupAI(self):
		return "GroupAI:"+str(self.xpoint)+","+str(self.ypoint)
		
class CreatureGroupAI(GroupAI):
	def __init__(self,creature):
		self.creaturefollow = creature
	def handleCreature(self,creature):
		if (self.attackNearby(creature)):
			return False#keep going
		if (self.creaturefollow == None or self.creaturefollow.dead):
			return True#if the creature is dead, unlink the ai
		if (creature.x == self.creaturefollow.x and creature.y == self.creaturefollow.x):
			return True#should not happen
		#find the first point of the bresham's line algorithm, using libtcod
		L.line_init(creature.x,creature.y,self.creaturefollow.x,self.creaturefollow.y)
		(x,y) = L.line_step()
		creature.move(x-creature.x,y-creature.y)
		return False#keep going
	def saveGroupAI(self):
		if (creature in Game()[A_MAP][L_CREATURES].list):
			return "CreatureGroupAI:"+str(Game()[A_MAP][L_CREATURES].list.index(creature))

#develop later	
"""class SmartGroupAI(GroupAI):
	def __init__(self,xpoint,ypoint):
		self.xpoint = xpoint
		self.ypoint = ypoint
		self.leader = None
		self.leaderdist = 0
		self.path = 0
	def takeTurn(self):
		if (self.attackNearby(creature)):
			return False#keep going
		if (creature.x == self.xpoint and creature.y == self.ypoint):
			return True#relinquish control once the point is reached
		if (self.leader == None or dist(self.xpoint,creature.x,self.ypoint,creature.y) <= self.leaderdist):
			self.leader = creature
		#find the first point of the bresham's line algorithm, using libtcod
		L.line_init(creature.x,creature.y,self.xpoint,self.ypoint)
		(x,y) = L.line_step()
		creature.move(x-creature.x,y-creature.y)
		return False#keep going"""



class CreatureAI:#the intelligence of a creature, decides to move for a creature
	def __init__(self,creature):
		self.creature = creature
		self.groupAI = None
	def takeTurn(self):
		if (self.groupAI != None):#move randomly if there is no collective mind to direct the creature
			if (self.groupAI.handleCreature(self.creature)):#returns True if the AI relinquishes control with no move
				self.groupAI = None#keep going as there is no return
			else:
				return 1
		#do the group AI
		for direction in DIRECTIONS:#scan adjacent creatures
			c = Game()[A_MAP][L_CREATURES][Point(self.creature.x+direction[0],self.creature.y+direction[1])]
			if (c != None and self.creature.canAttack(c) and self.creature.move(direction[0],direction[1])):#if it can attack and move there
				return 1
		#d=DIRECTIONS[Game()[A_RNG].nextInt(0,7)]#otherwise don't move at all
		#self.creature.move(d[0],d[1])			
		return 1
	def handleDeath(self):#delete the creature
		Game()[A_MAP][L_CREATURES].removeCreature(self.creature)
class PlayerAI(CreatureAI):#The player's brain, no taking turns
	doMove = False
	def takeTurn(self):
		self.doMove = True
		return 1
	def handleDeath(self):#set doMove to true so the player can exit the creature's body
		self.doMove = True
		print "YOU HAVE DIED"#XXX: need more intuitive death screen

def loadAI(string):#load the type of ai
	data = string.split(":")
	aitype = data[0]
	aidata = data[1].split(",")
	if (aitype == "CreatureGroupAI"):#do the creature ai
		return CreatureGroupAI(Game()[A_MAP][L_CREATURES].list[int(aidata)])
	if (aitype == "GroupAI"):#do the group ai
		return GroupAI(int(aidata[0]),int(aidata[1]))

class CreatureLayer(MapList,ChronosQueue):
	queue = dict()
	idcounter = 0L#the number of ids for the creatures
	def __init__(self,xlength,ylength):
		MapList.__init__(self,xlength,ylength)
	def addCreature(self,creature):#adds a creature to the layer
		self.list.append(creature)
	def removeCreature(self,creature):
		creature.rejectnotifications = True#it gets no notifications
		self.list.remove(creature)
		del creature#delete the creature
	def applyGroupAIToFaction(self,groupAI,faction):#applies a groupAI to a faction
		for creature in self.list:
			if (creature.faction == faction):
				creature.ai.groupAI = groupAI
	def load(self,data):#load the layer
		data = data.split("\n")
		creaturedata = data[0:data.index("\\BEGINAI")]
		for creaturemember in creaturedata:#load the creatures
			c = Creature()
			c.loadCreature(creaturemember.split(","))
		aidata = data[data.index("\\BEGINAI")+1:]
		for aimember in aidata:#load the ai
			aimemberdata = aimember.split(";")
			groupai = loadAI(aimemberdata[0])#load the type of ai
			for i in range(1,len(aimemberdata)):
				self.list[int(aimemberdata[i])].ai.groupAI = groupai#apply it to each creature that has it
				
			
		
	def save(self):#save the layer
		savestring = ""
		for creature in self.list:#save the creatures
			savestring += creature.saveCreature()
		groupAI = []#for the group ai
		groupAIstrings = []
		savestring+= "\\BEGINAI\n"#begin the AI
		for i in range(len(self.list)):#save each groupai with creatures
			theai = self.list[i].ai.groupAI
			if (theai != None):
				if (theai not in groupAI):#add the ai and creature
					groupAI.append(theai)
					newstring = theai.saveGroupAI()
					newstring += ";"+str(i)#save the ids
					groupAIstrings.append(newstring)
				else:
					groupAIstrings[groupAI.index(theai)] += ";" + str(i)#add the ai
		for string in groupAIstrings:#add the strings
			savestring += string+"\n"
		return savestring
